`include "defines.v"
module exu(
  input clk,
  input rst_n,
  input ctrl_exu_flush_i,
  /* isu to exu signal */
  input                 isu_exu_valid_i ,
  output                exu_isu_ready_o ,
  input                 alu_enable_i    ,
  input                 aluw_enable_i   ,
  input                 bru_enable_i    ,
  input                 csr_enable_i    ,
  input                 lsu_enable_i    ,
  input  [`OP_W-1:0]    isu_exu_op_i    ,
  input  [4:0]          isu_exu_rd_i    ,
  input                 isu_exu_rfwen_i ,
  input  [`XLEN-1:0]    isu_exu_op1_i   ,
  input  [`XLEN-1:0]    isu_exu_op2_i   ,
  input  [`XLEN-1:0]    isu_exu_imm_i   ,
  input  [`VADDR_W-1:0] isu_exu_pc_i    ,  
  input  [31:0]         isu_exu_inst_i  ,  
  input                 isu_exu_pre_i   ,
  /* exu bypass signal */
  output                 exu_isu_wen_o  ,  
  output [4:0]           exu_isu_addr_o ,  
  output [`XLEN-1:0]     exu_isu_data_o ,
  /* update ftonted */
  output                 bru_fronted_misPredict_o,
  output                 bru_fronted_valid_o     ,
  output                 bru_fronted_brunch_o    ,
  output [`VADDR_W-1:0]  bru_fronted_JumpOldPC_o ,
  output [`VADDR_W-1:0]  bru_fronted_misPrePC_o  ,
  /* exu to wbu */
  output                 exu_wbu_valid_o ,
  input                  wbu_exu_ready_i ,
  output                 exu_wbu_wen_o   ,
  output [4:0]           exu_wbu_rd_o    ,
  output [`XLEN-1:0]     exu_wbu_data_o  , 
  output [`VADDR_W-1:0]  exu_wbu_pc_o    ,
  output [31:0]          exu_wbu_inst_o  ,
  output [`EXCEPT:0]     exu_wbu_exception_o  ,
  output                 exu_wbu_wr_csr_en_o  ,
  output [11:0]          exu_wbu_wr_csr_addr_o,
  output [`XLEN-1:0]     exu_wbu_wr_csr_data_o,
  /* read csr port */
  output [11:0]          exu_csr_raddr_o,
  input  [`XLEN-1:0]     csr_exu_rdata_i,
  /* wbu bypass csr write port */
  input                  wbu_exu_wr_csr_en_i,
  input  [11:0]          wbu_exu_wr_csr_addr_i,
  input  [`XLEN-1:0]     wbu_exu_wr_csr_data_i,
  /* MMU port  */
  output                 exu_mmu_trans_valid_o,
  input                  mmu_exu_trans_ready_i,
  output [`VADDR_W-1:0]  exu_mmu_trans_vaddr_o,
  input  [`PADDR_W-1:0]  mmu_exu_trans_paddr_i,
  /* MEM 端口*/
  output                 lsu_dcache_paddr_valid_o , 
  output  [`PADDR_W-1:0] lsu_dcache_paddr_o       ,
  output  [7:0]          lsu_dcache_strb_o        ,
  output                 lsu_dcache_wen_o         ,
  output                 lsu_dcache_invalidate_o  ,
  output  [`XLEN-1:0]    lsu_dcache_wdata_o       ,
  input   [`XLEN-1:0]    dcache_lsu_rdata_i       ,
  input                  dcache_lsu_data_valid_i  ,
  // system message
  input                  clintEn,
  output                 clintInterrupt,
  output                 debug_selClint
`ifdef PREDICT_PC
  ,
  input                  baq_bru_valid_i ,
  input  [`VADDR_W-1:0]  baq_bru_dout_i  ,
  output                 bru_baq_ready_o 
`endif
);
reg exuValid;
reg                q_alu_enable_i    ;
reg                q_aluw_enable_i   ;
reg                q_bru_enable_i    ;
reg                q_csr_enable_i    ;
reg                q_lsu_enable_i    ;
reg [`OP_W-1:0]    q_isu_exu_op_i    ;
reg [4:0]          q_isu_exu_rd_i    ;
reg                q_isu_exu_rfwen_i ;
reg [`XLEN-1:0]    q_isu_exu_op1_i   ;
reg [`XLEN-1:0]    q_isu_exu_op2_i   ;
reg [`XLEN-1:0]    q_isu_exu_imm_i   ;
reg [`VADDR_W-1:0] q_isu_exu_pc_i    ;
reg [31:0]         q_isu_exu_inst_i  ;
reg                q_isu_exu_pre_i   ;

wire lsu_ready;
wire [`XLEN-1:0] alu_data;
wire [`XLEN-1:0] bru_data;
wire [`XLEN-1:0] csr_data;
wire [`XLEN-1:0] lsu_data;

wire isu_exu_hs = isu_exu_valid_i && exu_isu_ready_o;
wire exu_wbu_hs = exu_wbu_valid_o && wbu_exu_ready_i;
assign exu_isu_ready_o = ~exuValid || exu_wbu_hs;
assign exu_wbu_valid_o = q_alu_enable_i  ? exuValid : 
                         q_aluw_enable_i ? exuValid :
                         q_bru_enable_i  ? exuValid :
                         q_csr_enable_i  ? exuValid :
                         q_lsu_enable_i  ? lsu_ready && exuValid : 1'b0;

always@(posedge clk or negedge rst_n)
  if(~rst_n)
    exuValid <= 1'b0;
  else if(ctrl_exu_flush_i)
    exuValid <= 1'b0;
  else if(exu_isu_ready_o)
    exuValid <= isu_exu_valid_i;

always@(posedge clk)
  if(isu_exu_hs)begin
    q_alu_enable_i    <= alu_enable_i   ;
    q_aluw_enable_i   <= aluw_enable_i  ;
    q_bru_enable_i    <= bru_enable_i   ;
    q_csr_enable_i    <= csr_enable_i   ;
    q_lsu_enable_i    <= lsu_enable_i   ;
    q_isu_exu_op_i    <= isu_exu_op_i   ;
    q_isu_exu_rd_i    <= isu_exu_rd_i   ;
    q_isu_exu_rfwen_i <= isu_exu_rfwen_i;
    q_isu_exu_op1_i   <= isu_exu_op1_i  ;
    q_isu_exu_op2_i   <= isu_exu_op2_i  ;
    q_isu_exu_imm_i   <= isu_exu_imm_i  ;
    q_isu_exu_pc_i    <= isu_exu_pc_i   ;
    q_isu_exu_inst_i  <= isu_exu_inst_i ;
    q_isu_exu_pre_i   <= isu_exu_pre_i  ;
  end

/******************   ALU   ************************/
alu alu_u0(
  .mode     ( q_isu_exu_op_i  ),
  .rv32mode ( q_aluw_enable_i ),
  .op1      ( q_isu_exu_op1_i ),
  .op2      ( q_isu_exu_op2_i ),
  .out      ( alu_data        )
);
/******************   BRU   ************************/
bru bru_u0(
  .valid_i         ( q_bru_enable_i && exuValid ),
  .predictJump     ( q_isu_exu_pre_i            ),
  .mode            ( q_isu_exu_op_i             ),
  .op1             ( q_isu_exu_op1_i            ),
  .op2             ( q_isu_exu_op2_i            ),
  .imm             ( q_isu_exu_imm_i            ),
  .pc              ( q_isu_exu_pc_i             ),
  .out             ( bru_data                   ),
  .br_valid        ( bru_fronted_misPredict_o   ),
  .br_addr         ( bru_fronted_JumpOldPC_o    )
`ifdef PREDICT_PC
  ,
  .baq_bru_valid_i ( baq_bru_valid_i            ),
  .baq_bru_dout_i  ( baq_bru_dout_i             ),
  .bru_baq_ready_o ( bru_baq_ready_o            )
`endif
);
assign bru_fronted_valid_o    = q_bru_enable_i && exuValid;
assign bru_fronted_misPrePC_o = q_isu_exu_pc_i;
assign bru_fronted_brunch_o   = bru_fronted_misPredict_o;
/******************   CSR   ************************/
// bypass wbu csr
wire [11:0] csr_raddr;
wire csr_rd_hazard = wbu_exu_wr_csr_en_i && (wbu_exu_wr_csr_addr_i == csr_raddr);
wire [`XLEN-1:0] csr_rdata = csr_rd_hazard ? wbu_exu_wr_csr_data_i : csr_exu_rdata_i;
assign exu_csr_raddr_o = csr_raddr;
wire [`EXCEPT:0] csr_exception;
csrWriteBuffer csrWrite(
  .valid(q_csr_enable_i && exuValid),
  .illegalInstruction(1'b0),
  .inst        (q_isu_exu_inst_i),
  .op          (q_isu_exu_op_i),
  .op1         (q_isu_exu_op1_i),
  .op2         (q_isu_exu_op2_i),
  .out         (csr_data),
  .exception   (csr_exception),
  // 异步读CSR寄存器
  .rd_csr_addr ( csr_raddr ),
  .rd_csr_data ( csr_rdata ),
  // 同步写CSR寄存器
  .wr_csr_en   ( exu_wbu_wr_csr_en_o   ),
  .wr_csr_addr ( exu_wbu_wr_csr_addr_o ),
  .wr_csr_data ( exu_wbu_wr_csr_data_o )
);
/******************   LSU   ************************/
wire [`EXCEPT:0] lsu_exception;
assign lsu_exception[`ecall]   = 'd0;
assign lsu_exception[`ebreak]  = 'd0;
assign lsu_exception[`mret]    = 'd0;
assign lsu_exception[`ilginst] = 'd0;
assign lsu_exception[`sret   ] = 'd0;
assign lsu_exception[`uret   ] = 'd0;
assign lsu_exception[`clint  ] = clintEn && clintInterrupt;
assign lsu_exception[`fencei ] = q_lsu_enable_i && exuValid && ~ctrl_exu_flush_i && lsu_dcache_invalidate_o;
assign lsu_exception[`EXCEPT:`fencei+1] = 'd0;
lsu LSU(
  .clk(clk),
  .rst_n(rst_n),
  // wbu的刷新信号来临 不能启动本次访存 来保证内存一致性
  .valid_i ( q_lsu_enable_i && exuValid && ~ctrl_exu_flush_i),
  .op      ( q_isu_exu_op_i  ),
  .op1     ( q_isu_exu_op1_i ),
  .op2     ( q_isu_exu_op2_i ),
  .imm     ( q_isu_exu_imm_i ),
  .ready_o ( lsu_ready ), 
  .out     ( lsu_data ),

  .lsu_dcache_addr_valid_o  (lsu_dcache_paddr_valid_o ),
  .lsu_dcache_addr_o        (lsu_dcache_paddr_o       ),
  .lsu_dcache_strb_o        (lsu_dcache_strb_o        ),
  .lsu_dcache_wen_o         (lsu_dcache_wen_o         ),
  .lsu_dcache_invalidate_o  (lsu_dcache_invalidate_o  ),
  .lsu_dcache_wdata_o       (lsu_dcache_wdata_o       ),
  .dcache_lsu_rdata_i       (dcache_lsu_rdata_i       ),
  .dcache_lsu_data_valid_i  (dcache_lsu_data_valid_i  ),

  .lsu_exu_trans_valid_o    ( exu_mmu_trans_valid_o ),
  .exu_lsu_trans_ready_i    ( mmu_exu_trans_ready_i ),
  .lsu_exu_trans_vaddr_o    ( exu_mmu_trans_vaddr_o ),
  .exu_lsu_trans_paddr_i    ( mmu_exu_trans_paddr_i ),
  .clintEn                  ( clintEn               ),
  .clintInterrupt           ( clintInterrupt        ),
  .debug_selClint           ( debug_selClint        )
);

assign exu_wbu_wen_o   = q_isu_exu_rfwen_i && exuValid;
assign exu_wbu_rd_o    = q_isu_exu_rd_i;
assign exu_wbu_data_o  = q_alu_enable_i  ? alu_data  :
                         q_aluw_enable_i ? alu_data  : 
                         q_bru_enable_i  ? bru_data  : 
                         q_lsu_enable_i  ? lsu_data  : 
                         q_csr_enable_i  ? csr_data  : `ZERO;
assign exu_wbu_pc_o    = q_isu_exu_pc_i  ;
assign exu_wbu_inst_o  = q_isu_exu_inst_i;
assign exu_wbu_exception_o = csr_exception | lsu_exception;
// bypass exu to isu
assign exu_isu_wen_o    = exu_wbu_wen_o ;
assign exu_isu_addr_o   = exu_wbu_rd_o  ;
assign exu_isu_data_o   = exu_wbu_data_o;
endmodule